home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /*
- * Lock() and LLock() emulation. Takes care of expanding paths that contain
- * symlinks.
- * Call __plock() if you need a lock to the parent directory as used in
- * other packets, that way you always get the "right" thing
- */
-
- #define _KERNEL
- #include "ixemul.h"
- #include "kprintf.h"
-
- #include <stdlib.h>
- #include <string.h>
-
- static struct DevProc *get_device_proc (char *, struct DevProc *, int *);
- static int unslashify (char *);
-
- static void lock(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock, char *s)
- {
- sp->sp_Pkt.dp_Type = ACTION_LOCATE_OBJECT;
- sp->sp_Pkt.dp_Arg1 = lock;
- sp->sp_Pkt.dp_Arg2 = CTOBPTR(s);
- sp->sp_Pkt.dp_Arg3 = ACCESS_READ;
-
- PutPacket(handler, sp);
- __wait_sync_packet(sp);
- }
-
- static void unlock(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock)
- {
- sp->sp_Pkt.dp_Port = __srwport;
- sp->sp_Pkt.dp_Type = ACTION_FREE_LOCK;
- sp->sp_Pkt.dp_Arg1 = lock;
-
- PutPacket(handler, sp);
- __wait_sync_packet(sp);
- }
-
- static void readlink(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock, char *s)
- {
- sp->sp_Pkt.dp_Port = __srwport;
- sp->sp_Pkt.dp_Type = ACTION_READ_LINK;
- sp->sp_Pkt.dp_Arg1 = lock;
- sp->sp_Pkt.dp_Arg2 = (long)s; /* read as cstr */
- sp->sp_Pkt.dp_Arg3 = (long)s; /* write as cstr, same place */
- sp->sp_Pkt.dp_Arg4 = 255; /* what a BSTR can address */
-
- PutPacket(handler, sp);
- __wait_sync_packet(sp);
- }
-
- int is_pseudoterminal(char *name)
- {
- int i = 1;
-
- if (!memcmp(name, "/dev/", 5) || !(i = memcmp(name, "dev:", 4)))
- {
- if (i)
- name++;
- if ((name[4] == 'p' || name[4] == 't') && name[5] == 't'
- && name[6] == 'y' && name[7] >= 'p' && name[7] <= 'u'
- && strchr("0123456789abcdef", name[8]) && !name[9])
- return i + 4;
- }
- return 0;
- }
-
- BPTR
- __plock (const char *file_name, int (*last_func)(), void *last_arg)
- {
- BPTR parent_lock;
- struct MsgPort *handler;
- struct StandardPacket *sp;
- unsigned char *bstr;
- char *sep, *next = NULL, *cp;
- int len;
- /* true when we're processing the last element of name */
- int is_last;
- /* true after first pass, we should unlock all locks except the one
- * we get as a sideeffect of DeviceProc */
- int unlock_parent;
- int link_levels;
- int no_error;
- BPTR result;
- int res_res2;
- int omask;
- int is_fs;
- short is_root = u.u_is_root;
- struct DevProc *dp;
- char *orig_name, *name;
-
- KPRINTF (("__plock: file_name = %s, last_func = $%lx\n",
- file_name ? file_name : "(none)", last_func));
-
- /* ``fix'' until I find the real problem in pdksh.. */
- if (! file_name)
- return 0;
-
- /* need to operate on a backup of the passed name, so I can do
- * /sys -> sys: conversion in place */
- name = alloca (strlen (file_name) + 2);
- strcpy (name + 1, file_name);
- if (is_root)
- name[0] = '/';
- else
- name++;
- orig_name = name;
-
- /* now get a LONG aligned packet */
- sp = alloca (sizeof(*sp)+2);
- sp = LONG_ALIGN (sp);
- __init_std_packet(sp);
-
- /* allocate one BSTR-buffer. A length of 255 is enough, since a bstr
- * can't address any more ;-) */
- bstr = alloca (256 + 2);
- bstr = LONG_ALIGN (bstr);
-
- /* NOTE: although we don't use any DOS calls here, we have to block
- * any signals, since the locks we obtain in this function have to
- * be freed before anything else can be done. This function is
- * *not* reentrant in the sense that it can be interrupted without
- * being finished */
- omask = syscall (SYS_sigsetmask, ~0);
-
- dp = 0;
-
- retry_multi_assign:
-
- name = orig_name;
- if (ix.ix_flags & ix_translate_slash)
- unslashify (name);
-
- if (!strcmp(name, ":"))
- {
- result = 0;
- res_res2 = 6262; /* another special special (the root directory) */
- dp = 0;
- goto do_return;
- }
- if (!strcasecmp(name, "nil:") || !strcasecmp(name, "/nil") ||
- !strcmp(name, "/dev/null") || !strcmp(name, "dev:null"))
- {
- result = 0;
- res_res2 = 4242; /* special special ;-)))) */
- dp = 0;
- goto do_return;
- }
- if (is_pseudoterminal(name))
- {
- result = 0;
- res_res2 = 5252; /* special special ;-)))) */
- dp = 0;
- goto do_return;
- }
- if (!strcmp(name, "*") || !strcasecmp(name, "console:") ||
- !strcmp(name, "/dev/tty"))
- {
- handler = (struct MsgPort *)(((struct Process *)FindTask (0))->pr_ConsoleTask);
- parent_lock = 0;
- name = "*"; /* Apparently use of CONSOLE: gave problems with
- Emacs, so we continue to use "*" instead. */
- FreeDeviceProc (dp);
- is_fs = 0;
- dp = 0;
- }
- else
- {
- dp = get_device_proc (name, dp, &is_fs);
-
- handler = dp ? dp->dvp_Port : 0;
- parent_lock = dp ? dp->dvp_Lock : 0;
- }
-
- is_last = 0;
- unlock_parent = 0;
- link_levels = 0;
- result = 0;
-
- if (! handler)
- {
- res_res2 = ERROR_OBJECT_NOT_FOUND;
- goto do_return;
- }
-
- link_levels = 0;
-
- /* this seems logical, doesn't it? don't know ;-)) */
- sep = index (name, ':');
- if (sep) name = sep+1;
-
- do
- {
- KPRINTF (("__plock: solving for %s.\n", name));
-
- if (is_fs)
- {
- /* fetch the first part of "name", thus stopping at either a : or a /
- * next points at the start of the next directory component to be
- * processed in the next run
- */
-
- sep = index (name, ':');
- if (sep)
- {
- sep++; /* the : is part of the filename */
- next = sep;
- }
- else
- {
- sep = index (name, '/');
-
- /* map foo/bar/ into foo/bar, but keep foo/bar// */
- if (sep && sep[1]==0)
- {
- is_last = 1;
- next = sep;
- }
- else if (! sep)
- {
- sep = name + strlen (name);
- next = sep;
- is_last = 1;
- }
- else
- {
- if (ix.ix_flags & ix_translate_slash)
- for (next = sep + 1; *next == '/'; next ++) ;
- else
- next = sep + 1;
-
- /* if the slash is the first character, it means "parent",
- * so we have to pass it literally to Lock() */
- if (sep == name) sep = next;
- }
- }
- }
- else
- {
- sep = name + strlen (name);
- is_last = 1;
- }
-
- len = sep - name;
- if (len) bcopy (name, bstr + 1, len);
- *bstr = len;
-
- /* turn a ".." into a "/", and a "." into a "" */
-
- if (bcmp (bstr, "\2..", 3) == 0)
- {
- bstr[0] = 1; bstr[1] = '/';
- }
- else if (bcmp (bstr, "\1.", 2) == 0)
- bstr[0] = 0;
-
- do
- {
- int res = 1;
-
- sp->sp_Pkt.dp_Port = __srwport;
- if (! is_last)
- {
- lock(handler, sp, parent_lock, bstr);
- no_error = sp->sp_Pkt.dp_Res1 > 0;
- }
- else
- res = (*last_func)(sp, handler, parent_lock, CTOBPTR (bstr),
- last_arg, &no_error);
-
- is_root = 0;
- /* if no error, fine */
- if (no_error)
- break;
- if (is_fs && sp->sp_Pkt.dp_Res2 == ERROR_OBJECT_NOT_FOUND && !bcmp(bstr, "\1/", 2))
- {
- is_root = 1;
- orig_name = next - 1;
- *orig_name = '/';
- FreeDeviceProc (dp);
- dp = 0;
- if (unlock_parent)
- unlock(handler, sp, parent_lock);
- goto retry_multi_assign;
- }
- if (!res)
- break;
-
- /* else check whether ordinary error or really symlink */
- if (sp->sp_Pkt.dp_Res2 != ERROR_IS_SOFT_LINK) break;
-
- /* read the link. temporarily use our bstr as a cstr, thus setting
- * a terminating zero byte and skipping the length byte */
- bstr[*bstr + 1] = 0;
-
- readlink(handler, sp, parent_lock, bstr + 1);
-
- /* error (no matter which...) couldn't read the link */
- if (sp->sp_Pkt.dp_Res1 <= 0)
- {
- /* this is our error-"lock", so make sure it is really zero */
- sp->sp_Pkt.dp_Res1 = 0;
- break;
- }
-
- /* okay, new name. Set up as bstr and retry to lock it */
- *bstr = sp->sp_Pkt.dp_Res1;
-
- /* if the read name is absolute, we may have to change the
- * handler and the parent lock. Check for this */
- bstr[*bstr + 1] = 0;
-
- if ((cp = index ((char *)bstr + 1, ':')))
- {
- if (unlock_parent)
- unlock(handler, sp, parent_lock);
-
- /* if this is ":foobar", then the handler stays the same, and the
- * parent_lock gets zero. Don't need get_device_proc() to find
- * this out ;-) */
- if (cp == (char *)bstr+1)
- parent_lock = 0;
- else
- {
- /*
- * NOTE: Multiassigns are currently only supported as the first
- * part of a path name. I don't like the idea of setting up
- * another recursion level here just to parse them...
- *
- * This approach also makes symbolic links to non-fs devices
- * limited, which is bad. I'll HAVE to think something up
- * (so you can't for example have dev:tty -> con:0/0/640/100/tty)
- */
- handler = DeviceProc (bstr + 1); /* XXX fix !!! */
- parent_lock = IoErr ();
- }
- unlock_parent = 0;
-
- if (! handler)
- {
- /* interesting bug.. long not noticed... */
-
- sp->sp_Pkt.dp_Res1 = 0;
- if (! strcasecmp (bstr + 1, "nil:"))
- sp->sp_Pkt.dp_Res2 = 4242;
- else
- sp->sp_Pkt.dp_Res2 = ERROR_OBJECT_NOT_FOUND;
- break;
- }
- }
-
- ++link_levels;
- }
- while (link_levels < MAXSYMLINKS);
-
- if (link_levels == MAXSYMLINKS)
- {
- result = 0;
- res_res2 = ERROR_TOO_MANY_LEVELS;
- }
- else
- {
- result = sp->sp_Pkt.dp_Res1;
- res_res2 = sp->sp_Pkt.dp_Res2;
- }
-
- if (unlock_parent)
- unlock(handler, sp, parent_lock);
- else
- unlock_parent = (result != 0);
-
- parent_lock = result;
- name = next;
- }
- while (no_error && ! is_last);
-
- /* yes I know it's ugly ... */
- if (!no_error && res_res2 == ERROR_OBJECT_NOT_FOUND
- && dp && (dp->dvp_Flags & DVPF_ASSIGN))
- goto retry_multi_assign;
-
- do_return:
- FreeDeviceProc (dp);
-
- /* set up Result2 so that the IoErr() works */
- ((struct Process *)FindTask (0))->pr_Result2 = res_res2;
-
- syscall (SYS_sigsetmask, omask);
-
- KPRINTF (("__plock: returning %ld, res2 = %ld.\n", result, res_res2));
-
- return result;
- }
-
- /*
- * this is somewhat similar to DeviceProc(), but with the difference that it's
- * strictly passive, it won't start the handler, if it doesn't exist. This is
- * vital to be able to deal with stubborn console handlers, that don't answer
- * packets until they're really open..
- * Returns:
- * HAN_UNDEF device doesn't exist
- * HAN_CLONE_DEV device exists, handler zero
- * HAN_FS_DEV device exists, handler non-zero
- * HAN_NOT_A_DEV exists, but is not a device
- */
-
- #define HAN_UNDEF 0
- #define HAN_CLONE_DEV 1
- #define HAN_FS_DEV 2
- #define HAN_NOT_A_DEV 3
-
- static int
- find_handler (char *bstr)
- {
- struct RootNode *rn;
- struct DosInfo *di;
- struct DevInfo *dv;
- int res = HAN_UNDEF;
-
- /* could probably use less drastic measures under 2.0... */
- Forbid ();
- rn = (struct RootNode *) DOSBase->dl_Root;
- di = BTOCPTR (rn->rn_Info);
- for (dv = BTOCPTR (di->di_DevInfo); dv; dv = BTOCPTR (dv->dvi_Next))
- {
- if (! strncasecmp (bstr, BTOCPTR (dv->dvi_Name), bstr[0]+1))
- {
- if (dv->dvi_Type == DLT_DEVICE)
- res = dv->dvi_Task ? HAN_FS_DEV : HAN_CLONE_DEV;
- else
- res = HAN_NOT_A_DEV;
- break;
- }
- }
- Permit ();
- return res;
- }
-
- /*
- * feels very much like GetDeviceProc(), but works under 1.3 as well. Under
- * 2.0, we're using the dos-library GetDeviceProc(), under 1.3 that is
- * emulated with own structures.
- * is_fs is filled out with a best guess approach, since we can't use the
- * proper packet on a handler that isn't yet fully operating (as after just
- * calling DevProc)
- * if calling with prev!=0, is_fs is not touched.
- */
-
- static struct DevProc *
- get_device_proc (char *name, struct DevProc *prev, int *is_fs)
- {
- char *cp, *f = NULL;
- int len;
- struct DevProc *dp;
- int han = HAN_UNDEF;
- struct Process *this_proc = NULL;
- APTR oldwin = NULL;
-
- if (! prev)
- {
- /* have to prove the opposite */
- *is_fs = 1;
-
- cp = index (name, ':');
- if (cp && cp != name) /* ":..." has to be a filesystem */
- {
- len = cp - name;
- f = alloca (len + 1);
- f[0] = len;
- bcopy (name, f + 1, len);
-
- /* try to find it */
- han = find_handler (f);
-
- KPRINTF ((" find_handler(%s) = %ld\n", name, han));
-
- /* this might be wrong, we'll know more after GetDeviceProc() */
- if (han == HAN_CLONE_DEV)
- *is_fs = 0;
- }
- }
-
- if (ix.ix_flags & ix_no_insert_disk_requester)
- {
- this_proc = (struct Process *)FindTask (0);
- oldwin = this_proc->pr_WindowPtr;
- this_proc->pr_WindowPtr = (APTR)-1;
- }
- dp = GetDeviceProc (name, prev);
- if (ix.ix_flags & ix_no_insert_disk_requester)
- this_proc->pr_WindowPtr = oldwin;
-
- /* Second approach to verify, if a handler probably is a file system or not.
- * If the device is a filesystem, but didn't contain a volume before, then
- * the GetDeviceProc() call will have popped up the `please insert a disk'
- * requester. If the user obeyed, the handler will now exist. On the other
- * hand, if the device really is a clone device, it will still be one (ie. have
- * its task field zero), so check the device list again. */
-
- /* only for possible clone devices */
- if (han == HAN_CLONE_DEV)
- *is_fs = find_handler (f) != HAN_CLONE_DEV;
-
- return dp;
- }
-
- static int
- unslashify (char *name)
- {
- char *oname = name;
-
- if (index (name, ':'))
- return 0;
-
- while (oname[0] == '/' &&
- (oname[1] == '/' || !memcmp(oname + 1, "./", 2) || !memcmp(oname + 1, "../", 3)))
- while (*++oname == '.') ;
-
- if (!strcmp(oname, "/.") || !strcmp(oname, "/.."))
- oname[1] = '\0';
-
- /* don't (!) use strcpy () here, this is an overlapping copy ! */
- if (oname > name)
- bcopy (oname, name, strlen (oname) + 1);
-
- /* root directory */
- if (name[0] == '/' && name[1] == 0)
- {
- name[0] = ':';
- return 0;
- }
-
- if (name[0] == '/')
- {
- /* get the delimiter */
- char *cp = index (name + 1, '/');
- int shift = 0;
-
- /* if there is a separating (and not terminating) slash, shift a bit ;-) */
- if (cp)
- while (*cp == '/')
- {
- shift ++;
- cp ++;
- }
-
- /* is it a terminator (then discard it) or a separator ? */
- if (! cp || !*cp)
- {
- /* terminator */
- cp = name + strlen (name);
- bcopy (name + 1, name, cp - name);
- cp[-1-shift] = ':';
- cp[-shift] = 0;
- }
- else
- {
- /* separator */
- bcopy (name + 1, name, strlen (name) + 1);
- cp --;
- bcopy (cp, cp - (shift - 1), strlen (cp) + 1);
- cp[-shift] = ':';
- }
- }
- return 0;
- }
-